package com.showtao.common.security.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.cxf.common.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import com.showtao.common.enumeration.EErrorCode;
import com.showtao.common.exception.BizServiceException;
import com.showtao.common.security.SecurityContext;
import com.showtao.common.security.domain.Permission;
import com.showtao.common.security.domain.Role;
import com.showtao.common.security.domain.User;
import com.showtao.common.security.repository.PermissionRepository;
import com.showtao.common.security.repository.RoleRepository;
import com.showtao.common.security.repository.UserRepository;
import com.showtao.common.validation.ValidateException;
import com.showtao.front.domain.AdminMenu;
import com.showtao.front.domain.SystemLog;
import com.showtao.front.dto.RoleDto;
import com.showtao.front.dto.UserDto;
import com.showtao.front.dto.upstream.AdminMenuSavingDto;
import com.showtao.front.dto.upstream.UserSavingDto;
import com.showtao.front.repository.AdminMenuRepository;
import com.showtao.front.repository.SystemLogRepository;

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private PermissionRepository permissionRepository;
    @Autowired
    private SystemLogRepository systemLogRepository;
    @Autowired
    private AdminMenuRepository adminMenuRepository;
    @Autowired
    private SecurityContext securityContext;

    @Transactional( readOnly = true)
    public Map<String, List<AdminMenu>> getAvailableMenus(){
        List<AdminMenu> menus = adminMenuRepository.findAll(new Sort(new Order(Direction.ASC, "id")));
        Map<String, List<AdminMenu>> map = new TreeMap<String, List<AdminMenu>>();
        int i = 1;
        for(AdminMenu am : menus){
            if(am.getPermission() != null && 
                    securityContext.hasPermission(am.getPermission().getPermission())){
                List<AdminMenu> list = null;
                if(StringUtils.isEmpty(am.getCategory())){
                    list = new ArrayList<AdminMenu>();
                    list.add(am);
                    map.put("T:"+i, list);
                    i++;
                }else{
                    list = map.get(am.getCategory());
                    if(list == null){
                        list = new ArrayList<AdminMenu>();
                        map.put(am.getCategory(), list);
                    }
                    list.add(am);
                }
            }
        }
        return map;
    }
    
    @Transactional( readOnly = true)
    public User findUserByUserName(String userName) {
        return userRepository.findByUserName(userName);
    }
    
    @Transactional(readOnly = true)
    public Page<UserDto> findUsers(Pageable pageable) {
        Page<User> page = userRepository
                .findAllLeftJoinRoles(pageable);
        List<UserDto> list = new ArrayList<UserDto>();
        for (User u : page) {
            list.add(UserDto.from(u));
        }
        return new PageImpl<UserDto>(list, pageable,
                page.getTotalElements());
    }
    
    @Transactional(readOnly = true)
    public Page<RoleDto> findRoles(Pageable pageable) {
        Page<Role> page = roleRepository
                .findAllLeftJoinPermissions(pageable);
        List<RoleDto> list = new ArrayList<RoleDto>();
        for (Role r : page) {
            list.add(RoleDto.from(r));
        }
        return new PageImpl<RoleDto>(list, pageable,
                page.getTotalElements());
    }
    
    @Transactional(readOnly = true)
    public Page<Permission> findPermissions(Pageable pageable) {
        return permissionRepository.findAll(pageable);
    }
    
    @Transactional(readOnly = true)
    public Page<AdminMenu> findAdminMenus(Pageable pageable) {
        return adminMenuRepository.findAllJoinPermission(pageable);
    }
    
    @Transactional(readOnly = true)
    public Page<SystemLog> findLogs(Pageable pageable) {
        return systemLogRepository.findAll(pageable);
    }

    @Transactional( readOnly = true)
    public List<Role> findAllRoles(){
        return roleRepository.findAll();
    }
    
    @Transactional( readOnly = true)
    public List<Permission> findAllPermissions(){
        return permissionRepository.findAll();
    }
    
    @Transactional
    public void deleteUser(String userName){
        userRepository.delete(userName);
    }
    
    @Transactional
    public void saveMenu(AdminMenuSavingDto dto) {
        AdminMenu entity = null;
        Permission permission = null;
        if(dto.getId() != null){
            entity = adminMenuRepository.findOne(dto.getId());
            if(entity == null){
                throw new BizServiceException(EErrorCode.COMM_INTERNAL_ERROR,
                        "找不到id=" + dto.getId() + "的AdminMenu实体");
            }
        }else{
            entity = new AdminMenu();
            entity.setCreatedAt(Calendar.getInstance().getTime());
        }
        if(!StringUtils.isEmpty(dto.getPermission())){
            permission = permissionRepository.findOne(dto.getPermission());
            if(permission == null){
                throw new BizServiceException(EErrorCode.COMM_INTERNAL_ERROR,
                        "找不到permission=" + dto.getPermission() + "的Permission实体");
            }
        }
        entity.setCode(dto.getCode());
        entity.setName(dto.getName());
        entity.setPath(dto.getPath());
        entity.setUpdatedAt(Calendar.getInstance().getTime());
        entity.setPermission(permission);
        adminMenuRepository.save(entity);
    }
    
    @Transactional
    public void deleteMenu(Long id){
        adminMenuRepository.delete(id);
    }
    
    @Transactional
    public void saveUser(UserSavingDto dto) {
        User entity = null;
        if(!dto.isSaving()){
            entity = userRepository.findByUserName(dto.getUserName());
            if(entity == null){
                throw new BizServiceException(EErrorCode.COMM_INTERNAL_ERROR,
                        "找不到userName=" + dto.getUserName() + "的User实体");
            }
        }else{
            entity = userRepository.findByUserName(dto.getUserName());
            if(entity != null){
                BindingResult result = new BeanPropertyBindingResult(dto,"userSavingDto");
                result.addError(new FieldError("userSavingDto", "userName", "用户名"+dto.getUserName()+"已存在"));
                throw new ValidateException(result);
            }
            entity = new User();
            entity.setUserName(dto.getUserName());
            entity.setCreatedAt(Calendar.getInstance().getTime());
        }
        boolean needSetPwd = dto.isSaving();
        if(!StringUtils.isEmpty(dto.getPasswordAgain())){
            if(!dto.getPasswordAgain().equals(dto.getPassword())){
                BindingResult result = new BeanPropertyBindingResult(dto,"userSavingDto");
                result.addError(new FieldError("userSavingDto", "passwordAgain", "两次密码不一致"));
                throw new ValidateException(result);
            }
            needSetPwd = true;
        }
        if(needSetPwd){
            if(StringUtils.isEmpty(dto.getPassword())){
                BindingResult result = new BeanPropertyBindingResult(dto,"userSavingDto");
                result.addError(new FieldError("userSavingDto", "password", "不能为空"));
                throw new ValidateException(result);
            }
            entity.setPassword(dto.getPassword());
        }
        entity.setStatus(dto.getStatus());
        entity.setUpdatedAt(Calendar.getInstance().getTime());
        userRepository.save(entity);
        
        // update the roles associations
        entity.setRoles(new ArrayList<Role>());
        if(dto.getRoles()!=null){
            for (String role : dto.getRoles()) {
                if(StringUtils.isEmpty(role)){
                    continue;
                }
                Role r = roleRepository.getOne(role);
                if (r != null) {
                    entity.getRoles().add(r);
                }
            }
        }
        userRepository.save(entity);
    }
    
    @Transactional
    public void addSystemLog(String log, String createdBy){
        SystemLog entity = new SystemLog();
        entity.setLog(log);
        entity.setCreatedBy(createdBy);
        entity.setCreatedAt(Calendar.getInstance().getTime());
        systemLogRepository.save(entity);
    }
}
